/*
 * Copyright 2012-2021 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot;

import java.util.function.Supplier;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.Environment;
import org.springframework.util.Assert;

/**
 * 一个简单的对象注册表，在启动期间以及 {@link Environment} 后处理阶段可用，
 * 直到 {@link ApplicationContext} 准备完成为止。
 * <p>
 * 可用于注册可能创建成本较高，或需要在 {@link ApplicationContext} 可用之前共享的实例。
 * <p>
 * 该注册表使用 {@link Class} 作为键，意味着同一类型只允许存储单个实例。
 * <p>
 * 可以使用 {@link #addCloseListener(ApplicationListener)} 方法添加监听器，
 * 以便在 {@link BootstrapContext} 关闭且 {@link ApplicationContext} 完全准备好时执行操作。
 * 例如，某个实例可能选择将自己注册为常规的 Spring Bean，以供应用程序使用。
 *
 * @author Phillip Webb
 * @since 2.4.0
 * @see BootstrapContext
 * @see ConfigurableBootstrapContext
 */
public interface BootstrapRegistry {

	/**
	 * 向注册表中注册一个特定类型。如果指定的类型已被注册，且尚未作为 {@link Scope#SINGLETON 单例} 获取，
	 * 则会被替换。
	 * @param <T> 实例类型
	 * @param type 实例类型
	 * @param instanceSupplier 实例提供者
	 */
	<T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier);

	/**
	 * 如果指定类型尚未注册，则向注册表注册该类型。
	 * @param <T> 实例类型
	 * @param type 实例类型
	 * @param instanceSupplier 实例提供者
	 */
	<T> void registerIfAbsent(Class<T> type, InstanceSupplier<T> instanceSupplier);

	/**
	 * 判断指定类型是否已被注册。
	 * @param <T> 实例类型
	 * @param type 实例类型
	 * @return 如果该类型已被注册，则返回 {@code true}
	 */
	<T> boolean isRegistered(Class<T> type);

	/**
	 * 返回指定类型已注册的 {@link InstanceSupplier} 实例。
	 * @param <T> 实例类型
	 * @param type 实例类型
	 * @return 已注册的 {@link InstanceSupplier}，如果没有则返回 {@code null}
	 */
	<T> InstanceSupplier<T> getRegisteredInstanceSupplier(Class<T> type);

	/**
	 * 添加一个 {@link ApplicationListener}，当 {@link BootstrapContext} 关闭且
	 * {@link ApplicationContext} 准备完成时，会接收到 {@link BootstrapContextClosedEvent} 事件。
	 * @param listener 要添加的监听器
	 */
	void addCloseListener(ApplicationListener<BootstrapContextClosedEvent> listener);

	/**
	 * 用于在需要时提供实际实例的供应者。
	 *
	 * @param <T> 实例类型
	 * @see Scope
	 */
	@FunctionalInterface
	interface InstanceSupplier<T> {

		/**
		 * 在需要时创建实例的工厂方法。
		 * @param context {@link BootstrapContext}，可用于获取其他引导阶段的实例。
		 * @return 实例
		 */
		T get(BootstrapContext context);

		/**
		 * 返回提供实例的作用域。
		 * @return 作用域
		 * @since 2.4.2
		 */
		default Scope getScope() {
			return Scope.SINGLETON;
		}

		/**
		 * 返回一个具有更新后的 {@link Scope} 的新的 {@link InstanceSupplier}。
		 * @param scope 新的作用域
		 * @return 具有新作用域的新的 {@link InstanceSupplier} 实例
		 * @since 2.4.2
		 */
		default InstanceSupplier<T> withScope(Scope scope) {
			Assert.notNull(scope, "Scope must not be null");
			InstanceSupplier<T> parent = this;
			return new InstanceSupplier<T>() {

				@Override
				public T get(BootstrapContext context) {
					return parent.get(context);
				}

				@Override
				public Scope getScope() {
					return scope;
				}

			};
		}

		/**
		 * 用于根据给定实例创建 {@link InstanceSupplier} 的工厂方法。
		 * @param <T> 实例类型
		 * @param instance 实例
		 * @return 新的 {@link InstanceSupplier}
		 */
		static <T> InstanceSupplier<T> of(T instance) {
			return (registry) -> instance;
		}

		/**
		 * 用于根据 {@link Supplier} 创建 {@link InstanceSupplier} 的工厂方法。
		 * @param <T> 实例类型
		 * @param supplier 提供实例的供应者
		 * @return 新的 {@link InstanceSupplier}
		 */
		static <T> InstanceSupplier<T> from(Supplier<T> supplier) {
			return (registry) -> (supplier != null) ? supplier.get() : null;
		}

	}

	/**
	 * 实例的作用域。
	 * @since 2.4.2
	 */
	enum Scope {

		/**
		 * 单例实例。{@link InstanceSupplier} 只会被调用一次，
		 * 每次都会返回同一个实例。
		 */
		SINGLETON,

		/**
		 * 原型实例。每当需要实例时，都会调用 {@link InstanceSupplier}。
		 */
		PROTOTYPE

	}

}
